/**
 * @Title wheel-utils系统
 * @Package com.senyint.sys.utils.qrcode
 * @Description TODO
 * @Company 心医国际数字医疗系统（大连） 有限公司
 * @Copyright (c) 2016, 2017, Senyint. All rights reserved.
 * @date 2018年5月31日 下午7:03:51
 * @author WangNaifei
 * @version 1.0
 */

package com.senyint.sys.utils.zxing;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import javax.imageio.ImageIO;

import com.google.zxing.BarcodeFormat;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.DecodeHintType;
import com.google.zxing.EncodeHintType;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.NotFoundException;
import com.google.zxing.Result;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
import com.google.zxing.client.j2se.MatrixToImageConfig;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.common.HybridBinarizer;
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
import wang.naifei.core.utils.Assert;
import com.senyint.sys.utils.StringUtils;
import com.senyint.sys.utils.image.ImageFormat;

/**
 * {@code QRCodeUtils}
 * 
 * @Description: 二维码生成工具
 *               <p>
 * @author WangNaifei,WangNaifei@senyint.com
 * @date 2018年5月31日 下午7:03:51
 * @version 1.0
 * @since jdk1.7+
 */
public class ZxingCodeUtils {

	/**
	 * @Title: encodeQrCode
	 * @Description: 生成二维码
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年5月31日 下午11:49:14
	 * @version 1.0
	 * @param encodeParam 二维码相关配置
	 * @param stream
	 * @throws Exception
	 */
	public static void encodeQrCode(ZxingEncodeCodeParam encodeParam, OutputStream stream) throws Exception {
		String content = encodeParam.getContent();
		Assert.hasText(content, "QrCodeencodeParam 中 content 不能为空。");
		int width = encodeParam.getWidth();
		Assert.isTrue(width > 0, "QrCodeencodeParam 中 Width 不大于0。");
		int height = encodeParam.getHeight();
		Assert.isTrue(height > 0, "QrCodeencodeParam 中  height 不大于0。");
		// int width = 300;
		// int height = 300;
		// String content = "www.shuai.com 中";
		// 定义二维码的参数
		Map<EncodeHintType, Object> defHints = new HashMap<>();
		// 设置编码
		defHints.put(EncodeHintType.CHARACTER_SET, "utf-8");
		// 设置纠错等级
		defHints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.forBits(encodeParam.getLevel()));
		// 设置外边距
		defHints.put(EncodeHintType.MARGIN, encodeParam.getMargin());
		Map<EncodeHintType, ?> hints = encodeParam.getHints();
		// 二维码的参数
		if (hints != null) {
			defHints.putAll(hints);
		}
		BufferedImage encodeQrCode = encodeCode(content, BarcodeFormat.QR_CODE, width, height, hints, encodeParam.getOnColor(), encodeParam.getOffColor());
		String logoPath = encodeParam.getLogoPath();
		if (StringUtils.isNotEmpty(logoPath)) {
			InputStream openStream = null;
			if (logoPath.startsWith("http")) {
				URL url = new URL(logoPath);
				openStream = url.openStream();
			} else {
				openStream = new FileInputStream(logoPath);
			}
			writeLogoToQRCodeImage(encodeQrCode, openStream);
		}
		writeToStream(encodeQrCode, encodeParam.getImageFormat(), stream);
	}

	/**
	 * @Title: writeLogoToQRCodeImage
	 * @Description: 在二维码中间加上logo 需要容错率
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年5月31日 下午11:50:23
	 * @version 1.0
	 * @param image
	 * @param logoInputStream
	 * @throws IOException
	 */
	public static void writeLogoToQRCodeImage(BufferedImage image, InputStream logoInputStream) throws IOException {
		if (logoInputStream != null) {
			Graphics2D gs = image.createGraphics();

			int ratioWidth = image.getWidth() * 2 / 10;
			int ratioHeight = image.getHeight() * 2 / 10;
			// 载入logo
			Image img = ImageIO.read(logoInputStream);
			int logoWidth = img.getWidth(null) > ratioWidth ? ratioWidth : img.getWidth(null);
			int logoHeight = img.getHeight(null) > ratioHeight ? ratioHeight : img.getHeight(null);

			int x = (image.getWidth() - logoWidth) / 2;
			int y = (image.getHeight() - logoHeight) / 2;

			gs.drawImage(img, x, y, logoWidth, logoHeight, null);
			gs.setColor(Color.black);
			gs.setBackground(Color.WHITE);
			gs.dispose();
			img.flush();
		}
	}

	/**
	 * @Title: encodeBarCode
	 * @Description: 输出条形码
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年6月1日 上午9:39:49
	 * @version 1.0
	 * @param encodeParam
	 * @param stream
	 * @throws IOException
	 * @throws WriterException
	 */
	public static void encodeBarCode(ZxingEncodeCodeParam encodeParam, OutputStream stream) throws WriterException, IOException {
		String content = encodeParam.getContent();
		Assert.hasText(content, "QrCodeGenerateParam 中 content 不能为空。");
		int width = encodeParam.getWidth();
		Assert.isTrue(width > 0, "QrCodeencodeParam 中 Width 不大于0。");
		int height = encodeParam.getHeight();
		Assert.isTrue(height > 0, "QrCodeencodeParam 中  height 不大于0。");

		int minWidth = 3 + // start guard
				(7 * 6) + // left bars
				5 + // middle guard
				(7 * 6) + // right bars
				3 // end guard
				+ 7;// 最小自己可识别 102 = 95+7
		minWidth = Math.max(minWidth, width);

		Map<EncodeHintType, ?> hints = encodeParam.getHints();

		BufferedImage encodeQrCode = encodeCode(content, BarcodeFormat.CODE_128, width, height, hints, encodeParam.getOnColor(), encodeParam.getOffColor());
		String logoPath = encodeParam.getLogoPath();
		if (StringUtils.isNotEmpty(logoPath)) {
			InputStream openStream = null;
			if (logoPath.startsWith("http")) {
				URL url = new URL(logoPath);
				openStream = url.openStream();
			} else {
				openStream = new FileInputStream(logoPath);
			}
			writeLogoToBarCodeImage(encodeQrCode, openStream);
		}
		writeToStream(encodeQrCode, encodeParam.getImageFormat(), stream);
	}

	/**
	 * @Title: writeLogoToBarCodeImage
	 * @Description: 给条形码加logo
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年6月1日 上午12:07:39
	 * @version 1.0
	 * @param encodeQrCode
	 * @param openStream
	 */
	public static void writeLogoToBarCodeImage(BufferedImage encodeQrCode, InputStream openStream) {
		// TODO Auto-encoded method stub

	}

	/**
	 * @Title: encodeCode
	 * @Description: TODO
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年6月1日 上午10:06:41
	 * @version 1.0
	 * @param content
	 * @param barcodeFormat
	 * @param width
	 * @param height
	 * @param hints
	 * @param onColor
	 * @param offColor
	 * @return
	 * @throws WriterException
	 * @throws IOException
	 */
	public static BufferedImage encodeCode(String content, BarcodeFormat barcodeFormat, int width, int height, Map<EncodeHintType, ?> hints, int onColor, int offColor) throws WriterException, IOException {
		BitMatrix matrix = new MultiFormatWriter().encode(content, barcodeFormat, width, height, hints);
		MatrixToImageConfig config = new MatrixToImageConfig(onColor, offColor);
		return MatrixToImageWriter.toBufferedImage(matrix, config);
	}

	/**
	 * @Title: writeToStream
	 * @Description: 输出二维码
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年5月31日 下午11:49:58
	 * @version 1.0
	 * @param bufferedImage
	 * @param format
	 * @param stream
	 * @throws IOException
	 */
	public static void writeToStream(BufferedImage bufferedImage, String format, OutputStream stream) throws IOException {
		if (!ImageIO.write(bufferedImage, format, stream)) {
			throw new IOException("Could not write an image of format " + format);
		}
	}

	/**
	 * @Title: ReadQrCode
	 * @Description: 解析二维码
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年5月31日 下午11:50:09
	 * @version 1.0
	 * @param inputStream
	 * @return
	 * @throws IOException
	 * @throws NotFoundException
	 */
	public static Result decodeQrCode(InputStream inputStream) throws IOException, NotFoundException {
		if (inputStream == null) {
			return null;
		}
		BufferedImage image = ImageIO.read(inputStream);
		// 定义二维码参数
		Map<DecodeHintType, String> hints = new HashMap<>();
		hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
		return decodeCode(image, hints);
	}

	/**
	 * @Title: decodeBarCode
	 * @Description: 解析条形码
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年6月1日 上午12:18:01
	 * @version 1.0
	 * @param inputStream
	 * @return
	 * @throws IOException
	 * @throws NotFoundException
	 */
	public static Result decodeBarCode(InputStream inputStream) throws IOException, NotFoundException {
		if (inputStream == null) {
			return null;
		}
		BufferedImage image = ImageIO.read(inputStream);
		Map<DecodeHintType, Object> hints = new HashMap<>();
		hints.put(DecodeHintType.CHARACTER_SET, "utf-8");
		return decodeCode(image, hints);
	}

	/**
	 * @Title: decodeCode
	 * @Description: 解析
	 * @author WangNaifei,WangNaifei@senyint.com
	 * @date 2018年6月1日 上午10:08:14
	 * @version 1.0
	 * @param image
	 * @param hints
	 * @return
	 * @throws NotFoundException
	 */
	public static Result decodeCode(BufferedImage image, Map<DecodeHintType, ?> hints) throws NotFoundException {
		BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(new BufferedImageLuminanceSource(image)));
		return new MultiFormatReader().decode(bitmap, hints);
	}

	public static void main(String[] args) throws FileNotFoundException, Exception {
		File file = new File("d:/barcode.jpg");
		// File file = new File("d:/123.jpg");
		ZxingEncodeCodeParam zxingEncodeCodeParam = new ZxingEncodeCodeParam("ABd6923450657");
		zxingEncodeCodeParam.setWidth(100);
		zxingEncodeCodeParam.setHeight(50);
		zxingEncodeCodeParam.setImageFormat(ImageFormat.jpg.name());
		ZxingCodeUtils.encodeBarCode(zxingEncodeCodeParam, new FileOutputStream(file));
		Result decodeBarCode = ZxingCodeUtils.decodeBarCode(new FileInputStream(file));
		System.out.println(decodeBarCode.toString());
		System.out.println(decodeBarCode.getBarcodeFormat());
	}
}
